package main

// #cgo LDFLAGS: -L/Users/wang/CLionProjects/unicorn/build
// #cgo CFLAGS: -I/opt/homebrew/Cellar/capstone/5.0.6/include/
import "C"
import (
	"bc/gapstone"
	"encoding/hex"
	"fmt"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
	"os"
	"strings"
	"time"
)

var asm = `88f882528808b2728907895289aba472ec0308aaed0309aa85010d8b`
var base uint64 = 0x10000000

func addHooks(mu uc.Unicorn, cs gapstone.Engine, base, end uint64, f *os.File) {

	mu.HookAdd(uc.HOOK_CODE, func(mu uc.Unicorn, addr uint64, size uint32) {
		_, err := mu.MemRead(addr, uint64(size))
		if err != nil {
			return
		}
		f.WriteString(fmt.Sprintf("at %x ", addr))
		var r1 = make([]string, 31)
		for i := 199; i < 227; i++ {
			r, _ := mu.RegRead(i)
			r1[i-199] = fmt.Sprintf("x%d= 0x%x", i-199, r)
		}
		f.WriteString(strings.Join(r1, ","))
		f.WriteString("\n")
		//fmt.Printf("0x%x\n", b)
		//_, err1 := cs.Disasm(read, 4, 1)
		//if err1 != nil {
		//	return
		//}
		//for i := 0; i < len(disasm); i++ {
		//	fmt.Printf("HOOK_INSN: base:0x%x %s %s\n", addr-base, disasm[i].Mnemonic, disasm[i].OpStr)
		//}

	}, base, base+end)

}

func run() error {
	asm = strings.ReplaceAll(asm, "\n", "\\x")

	asm = strings.ReplaceAll(asm, " ", "\\x")
	fmt.Println(asm)
	code, err := hex.DecodeString(asm)
	if err != nil {
		return err
	}
	// set up unicorn instance and add hooks
	mu, err := uc.NewUnicorn(uc.ARCH_ARM64, uc.MODE_ARM)
	if err != nil {
		return err
	}

	cs, err := gapstone.New(gapstone.CS_ARCH_ARM64, gapstone.CS_MODE_ARM)
	if err != nil {
		return err
	}
	f, _ := os.OpenFile("/tmp/a2.txt", os.O_WRONLY|os.O_CREATE, 644)
	addHooks(mu, cs, base, 0x1000, f)
	// map and write code to memory
	if err := mu.MemMap(base, 0x1000); err != nil {
		return err
	}
	if err := mu.MemWrite(base, code); err != nil {
		return err
	}
	t1 := time.Now()
	//1000000
	for i := 0; i < 0x1000; i++ {
		// start emulation
		if err := mu.Start(base, base+uint64(len(code))); err != nil {
			return err
		}
		//mu.MemRead(base+uint64(len(code))-4, 4)
		// read back example register
		//rdx, err := mu.RegRead(uc.ARM64_REG_X5)
		//if err != nil {
		//	return err
		//}
		//fmt.Printf("ARM64_REG_X5 is now: %x\n", rdx)
	}
	fmt.Printf("%f\n", time.Since(t1).Seconds())
	f.Close()
	return nil
}

func main() {
	if err := run(); err != nil {
		fmt.Println(err)
	}
}
